﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Pavilion
{
    public class CollectionBinding<SourceT, TargetT>
    {
        private IItemCreator sourceCreator;
        public NotifiableCollection<SourceT> SourceCollection { get; private set; }
        public NotifiableCollection<TargetT> TargetCollection { get; private set; }

        public CollectionBinding(NotifiableCollection<TargetT> targetCollection, IItemCreator sourceCreator)
        {
            this.sourceCreator = sourceCreator;
            this.TargetCollection = targetCollection;

            SourceCollection = new NotifiableCollection<SourceT>();

            foreach (TargetT targetItem in TargetCollection)
                SourceCollection.Add(CreateSourceItem(targetItem));

            RegisterCollectionEvents();
        }

        private void RegisterCollectionEvents()
        {
            SourceCollection.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(SourceCollection_CollectionChanged);
            TargetCollection.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(TargetCollection_CollectionChanged);
        }

        private void UnregisterCollectionEvents()
        {
            SourceCollection.CollectionChanged -= new System.Collections.Specialized.NotifyCollectionChangedEventHandler(SourceCollection_CollectionChanged);
            TargetCollection.CollectionChanged -= new System.Collections.Specialized.NotifyCollectionChangedEventHandler(TargetCollection_CollectionChanged);
        }

        private SourceT CreateSourceItem(TargetT targetItem)
        {
            object item = sourceCreator.CreateSourceItem(targetItem);

            if (item == null)
                throw new Exception("The source creator cannot create a null item.");

            return (SourceT)item;
        }

        private TargetT CreateTargetItem(SourceT sourceItem)
        {
            object item = sourceCreator.CreateTargetItem(sourceItem);

            if (item == null)
                throw new Exception("The source creator cannot create a null item.");

            return (TargetT)item;
        }

        private void SourceCollection_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            UnregisterCollectionEvents();

            if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Move)
                TargetCollection.Move(e.OldStartingIndex, e.NewStartingIndex);
            else if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Replace)
            {
                TargetCollection.RemoveAt(e.OldStartingIndex);
                TargetCollection.Insert(e.NewStartingIndex, CreateTargetItem(SourceCollection[e.NewStartingIndex]));
            }
            else if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Remove)
            {
                TargetCollection.RemoveAt(e.OldStartingIndex);
            }
            else if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
            {
                TargetCollection.Insert(e.NewStartingIndex, CreateTargetItem((SourceT)e.NewItems[0]));

                if (e.NewItems.Count > 1)
                    throw new NotImplementedException();
            }
            else
                throw new NotImplementedException();

            RegisterCollectionEvents();
        }

        private void TargetCollection_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            UnregisterCollectionEvents();

            if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Move)
                SourceCollection.Move(e.OldStartingIndex, e.NewStartingIndex);
            else if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Replace)
            {
                SourceCollection.RemoveAt(e.OldStartingIndex);
                SourceCollection.Insert(e.NewStartingIndex, CreateSourceItem(TargetCollection[e.NewStartingIndex]));
            }
            else if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Remove)
            {
                SourceCollection.RemoveAt(e.OldStartingIndex);
            }
            else if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
            {
                SourceCollection.Insert(e.NewStartingIndex, CreateSourceItem((TargetT)e.NewItems[0]));

                if (e.NewItems.Count > 1)
                    throw new NotImplementedException();
            }
            else
                throw new NotImplementedException();

            RegisterCollectionEvents();
        }
    }
}
